'''
 v2.0 GUI 入口程序
'''
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QFrame, QLabel, QLineEdit, QPushButton, QTextEdit
import json
import os
import requests
from bs4 import BeautifulSoup
from tqdm import tqdm
import asyncio
import aiofiles  # 异步操作文件

# 定义全局cookies
cookies_All = None


def saveCookies(cookies, self):
    # 将 cookies 存入 txt 文件
    with open('cookies.txt', 'w', encoding='utf-8') as f:
        f.write(cookies)
        print("登录信息已经保存.")
        self.tip_text.append("登录信息已经保存!")


# 清除txt文件全部内容
def clear_txt_file_cookies():
    with open('cookies.txt', 'w') as file:
        file.truncate(0)


# 从本地获取token
def getCkFromTxt():
    # 从 txt 文件中取出 cookies 并使用
    try:
        with open('cookies.txt', 'r', encoding='utf-8') as f:
            return f.read()
    except EOFError:
        # 处理文件为空的情况
        return False


# 判断本地是否有token
def judgeHasCk(self):
    # 从 txt 文件中取出 cookies 并使用
    try:
        with open('cookies.txt', 'r', encoding='utf-8') as f:
            cookies_has = f.read()
            if cookies_has != '':
                return True
            else:
                return False
    except EOFError:
        # 处理文件为空的情况
        return False


# json解析函数
def parse_json_with_error_handling(json_str):
    try:
        # 尝试解析 JSON 字符串
        parsed_json = json.loads(json_str)
        return parsed_json
    except json.JSONDecodeError as e:
        # 获取错误信息
        error_message = str(e)
        # print(f"JSON 解析错误(不用管): {error_message}\n")
        print(f"JSON 努力解析中...\n")
        # 找到错误位置
        error_pos = e.pos
        # 尝试截断字符串并重新解析
        truncated_json = json_str[:error_pos]
        try:
            parsed_json = json.loads(truncated_json)
            return parsed_json
        except json.JSONDecodeError:
            print("即使截断后也无法解析 JSON 字符串")
            return None


# 向手机号发送验证码
def sentCodeWithPhone(phoneNum, self):
    """
    向手机号发送验证码
    :return:
    """
    yourPhone = phoneNum
    # URL
    url = 'https://mobile.yangkeduo.com/proxy/api/api/sigerus/mobile/code/request'

    # 参数
    params = {
        'pdduid': 0,
    }

    # 请求体 - 如果是JSON格式
    json_data = {
        "platform": "4",
        "fingerprint": "{\"innerHeight\":1229,\"innerWidth\":2496,\"devicePixelRatio\":0.75,\"availHeight\":1032,\"availWidth\":1920,\"height\":1080,\"width\":1920,\"colorDepth\":24,\"locationHerf\":\"https://mobile.yangkeduo.com/login.html?from=https%3A%2F%2Fmobile.yangkeduo.com%2Fpjlkvgcf.html%3F_live_share_token%3DCRQ46EEGIEMXATU2NPB2SDCKSN3FIZZTCITIONO7ZRLOQH5CWIWKP4JAI64MZJ3GI2SYHRBQQULEM7UK32D2T6E5NKY6GMUWLFIZ6UQJRRAL5LPF45XVKHN4INYVOQIA%26refer_share_channel%3Dcopy_link%26mall_id%3D649584606%26page_from%3D601129%26refer_share_id%3DG3OiO3IWXbeIHPDNbB2HtJwO72WRlQU0%26refer_share_uin%3DUYSGSN6OZYI3ZCQRREZTO3FW4Y_GEXDA%26_x_share_id%3DG3OiO3IWXbeIHPDNbB2HtJwO72WRlQU0%26refer_page_name%3Dlogin%26refer_page_id%3D10169_1722933421511_zeph4lzajg%26refer_page_sn%3D10169&_x_share_id=G3OiO3IWXbeIHPDNbB2HtJwO72WRlQU0&refer_page_name=yhzb_bfy&refer_page_id=31430_1722934355555_3gz7a8wwcm&refer_page_sn=31430\",\"referer\":\"yhzb_bfy\",\"timezoneOffset\":-480,\"navigator\":{\"appCodeName\":\"Mozilla\",\"appName\":\"Netscape\",\"hardwareConcurrency\":8,\"language\":\"zh-CN\",\"cookieEnabled\":true,\"platform\":\"Win32\",\"doNotTrack\":null,\"ua\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36\",\"vendor\":\"Google Inc.\",\"product\":\"Gecko\",\"productSub\":\"20030107\"}}",
        "touchevent": "{}",
        "mobile": yourPhone,
        "screen_token": "0asAfxcydiKyj9maQBRvgBfP-3zpqlZ-TjmMYpiLxxhxobeJD1ye5_DV3t8keO8U3eMu_yKswv8BSCTs_NrtwXNLBsXmGEp25hn4dbSY0SgL5xX2ZIPL04GZZqvoQ4spiboF2mPMgYM-au7b4-aLDFspFK1cZyxKII_lnim87uSjRyeqiLFftKXfrGtsoYfUB0BEoKBY3-Sczr4gRE7z5U4QLsjHAUVuzVGkxjAVBbNOAao54TgpoJYo51mCRbDNwJNgtKkN_yN4_ykmT8ACnbybS7K_qllTjAI0pCBJzYg4SpQZtoauj4czFqobkiCzAOto9g8inD23u18vNgcokU91nb-dn7r04Y17T2joWQCw8J6Fkr9pN6bHPgACrcBACwM50TnsKay2pro1XQDnHWYGxLi8cWFHE4OIOIsgJkrP4dbSHzvB0BpejX5FZy147eAqS4ZGkOtUtMyPCFuRMEHJMvj2GpjoxBn3wqWG8k_k7JlZCWZQsuFbbdOKr2xvmIYKVdUTUw6SzLUQi5cnNzDBtzYmM2OpL5eCWKaHxWjbFbWLoX3Wz8ExR6abdL7jbJ37B_-jF-oVK3bM2f7thqhGbiALf7t6XNYu1mnwv1ZSfZ_YVAT4PvCI9jcwYwalihwdDAYsAM_eeLnOEFa1i1uhXABh3BzmP5IrpMlNsqAwEfOXZRjPFwzRyYz55FNnSprTObqkdDVUKrbd3-UYqKcc4qXy5G-1vgqfN1y5IDZA6ioCCKHwktQiB2x3R2a34PjXQer1bOKN_5ny4bIfxGxXfJlt8m6qMsd_1tyOy_Tf2AElgwaTLV7E2P3kHCDq3qMX9mkLX1yugq4t70c8PuY-hMpB_DTFRN5vlJhZdd4efkX4EIUB9xj0ao-PgsMKjMhqXT_aigTK3wZpfg1w8wMX2KCVNVsyYfECL2W5O-SiadPkzCU"
    }

    # 或者如果你的数据不是JSON格式
    # data = {
    #     'field1': 'value3',
    #     'field2': 'value4'
    # }

    # 发送请求
    response = requests.post(url, params=params, json=json_data)
    # 检查响应状态码
    if response.status_code == 200:
        # 成功获取响应
        print("Success!已发送验证码!")
        self.tip_text.append("发送验证码至:" + phoneNum)
        # print(response.json())  # 假设响应是JSON格式
    else:
        print(f"验证码发送失败,请联系管理员!")


# 根据手机号+验证码登录,并返回登录cookies
def getCookiesWithCodeAndLogin(phoneNum, codeNum, self):
    # 响应成功之后  输入验证码登录请求
    # yourCode = '394122'
    yourCode = codeNum
    yourPhone = phoneNum
    # print(f"您输入的内容是: {yourCode}")
    # URL
    url_code = 'https://mobile.yangkeduo.com/proxy/api/api/sigerus/login_mobile'

    # 参数
    params_code = {
        'pdduid': 0,
    }
    json_code = {
        "mobile": yourPhone,
        "code": yourCode,
        "anti_content": "0asWtqly7ifs8suaTPcQdML5bUWpG9FVFqBJQL8JDDk1trYg3chLKFJJbYmo68JJki3QNymBstffMP8p5GzJoZ0I02KNJNGwFfB_vk4tBsv4fsk6_kZT_1_SX8f3bsoCtrSiRXZ2zDX4E-rD2Qm4Qu7ywQkHKVOLO7_X-jE-k7NQCwjnS2BcuqVP32x8BoKARwMr2wLMPnTAVaAqEF6hbpT3IYQK-ZvzE_Wz2_c0_9nWCnJXQXmqlAkjXG2Cyp63-gXTXL6UQ98nEfPAVIC_hrgJODofpiVmR2Uq2mO4gLsStiPXWlbTYfnWxSC2cAZXRgoagtOP9bpTfPAx-m4RJSd2r3ZgNsTn83ZGRg5VVM9encVJfqm84GenWlSm4sN8fOXen8nSq0hrNv3nPgEZ4oel4UA53lVxKxPASdA9BBPRaG3tmJ7ZGLSnqCOA-Ml5ttVnJV-SZfLh-_mztEcFZKEBzc0-Lzdnv8zqJuo3YOHNxyjuTiH4PfLGD7LbiPer-qROqeO5Aj0QtTVlp6OIq-4qEA7nNjlXHCE7jooce_YWD85i8g9oHKNIHpvJBFyrD_XDGIyMTfbPGzQcNAlQ7ZJOPWxV-OLc19ZgAPf7fuJWNbyIB-_FSVrmpQtaUk_Lz4TkbZ6-I4f6j-6wP0rqKVWcLhfIsqStDBl5TZsJvbrTdDSMqUyjmsl9hQstGesK-Df1cw5VFS2ka18qh6xFdhXDQBUo0mRPoB8xTRui_kqDmMPprn-P4-nGMFhqmBZkJlgZTz1JvaVLGP0H-4SbjM8cC9EdkKxttXUQk1lGFYexNzp7FcHjFWIypgSn1ZVvGakGoUT9rZLM4Pmq_GkUqdkYSKIEOzpgHGMn-3WTVsMHkuGiov9SdKk39JkxKweDVajnwDUkb8OEZEKlB5iIFFXAPOdwumCXOu0SCEzJSk9qGVGcOcDf0IM9EzGrtP-hCvz2gN4sWZSZVvfD7V9",
        "add_cookie": True,
        "login_app_id": 155
    }
    # 发送请求
    response = requests.post(url_code, params=params_code, json=json_code)

    # 检查响应状态码
    if response.status_code == 200:
        # 成功获取响应
        print("Success!已成功登录!")
        self.tip_text.append("登录成功!")
        # print('全部cookie', response.cookies)
        # print("响应内容",response.cookies)
        for cookie in response.cookies:
            if cookie.name == 'PDDAccessToken':
                # print('真实PDDAccessToken',cookie.value)
                return cookie.value


# 根据单个直播地址url和cookies获取直播视频  异步函数
async def downVideoByUrl(url):
    # self.tip_text.append("开始下载视频!")
    # 本地获取cookie
    global cookies_All
    cookies_All = getCkFromTxt()
    if cookies_All == '':
        print("cookies为空,请重新登录!")
        # self.tip_text.append("本地信息为空,请重新登录!")
        return

    # 添加 cookies
    cookies_value = cookies_All
    print("获取cookie..." + cookies_value)

    # print(cookies_value)
    # cookies = {
    #     'PDDAccessToken': cookies_value
    # }
    cookies = {
        'PDDAccessToken': cookies_value
        # 'PDDAccessToken':'Z7GWKTNRUMKHMLBHE3M2BZKAUIB4DLNVGOW65R74JHSBVR2XK4CQ12022a1'
    }
    #     {
    #     # 成功跳过验证   但是肯定有时间  还得重新登录   收到个短信?
    #     'PDDAccessToken': 'H2ABJ6UB6QVIPXA4O6FO4RDZPGL5Y54WGNKBCGDPAUNSRVVJJEQA12022a1',
    #     # 更多 cookies...
    # }

    # 发送带有 cookies 的请求并获取响应
    response = requests.get(url, cookies=cookies)
    print("响应内容")

    # 需要下来的视频流地址
    needDown = ''
    # 直播间名称
    homeName = ''
    # 买的东西
    showName = ''

    # 检查请求是否成功
    if response.status_code == 200:
        print("请求成功")
        # 解析HTML
        soup = BeautifulSoup(response.text, 'html.parser')

        # 查找所有的 <script> 标签
        script_tags = soup.find_all('script')
        # 输出每个 <script> 标签的内容
        i = 1
        for script in script_tags:
            # 输出 <script> 标签的文本内容
            script_content = script.string
            if script_content is None:
                # 如果没有直接的 string 属性，尝试获取所有文本
                script_content = script.get_text()
            if "window.rawData" in script_content:
                objStr = script_content.replace(' window.rawData=', '')
                objStr = objStr.strip()
                # print(objStr)
                if "null;" == objStr:
                    print("登录失效,请重新登录!")
                    # self.tip_text.append("尚未登录,请重新登录! 或者检查直播是否开播!")
                    clear_txt_file_cookies()  # 清除本地的cookies   以达到重新登录的效果
                    raise ValueError("尚未登录,请重新登录! 或者检查直播是否开播!")
                else:
                    # 去转义
                    objJson = parse_json_with_error_handling(objStr)
                    # 直播间名称
                    homeName = objJson['store']['shareInfo']['pinxiaoquanShareTitle']
                    print("直播间名称:" + homeName)
                    # self.tip_text.append("直播间名称:" + homeName)
                    # 所有视频地址
                    playUrlList = objJson['store']['playUrlList']
                    for index, item in enumerate(playUrlList):
                        if index == 0:
                            # 获取第一个视频地址
                            playUrl = item['playUrl']
                            # 下载第一个
                            needDown = playUrl
                    if homeName == '' or homeName is None:
                        print('此直播间已经关播')
                        # self.tip_text.append("此直播间已经关播,请更换直播源!若更换直播源后仍为此错误,可能是此号登录频繁,请手动将此号登陆至电脑端观察是否能查看直播;若手动登录也不能看,"
                        # "请更换账户登录!!!")
                        raise ValueError("此直播间已经关播,请更换直播源!若更换直播源后仍为此错误,可能是此号登录频繁,请手动将此号登陆至电脑端观察是否能查看直播;若手动登录也不能看,"
                                         "请更换账户登录!!!")
                    else:
                        # self.tip_text.append("正在获取" + homeName + "的内容...")
                        print(homeName + 'start...')
                    # 设置输出文件地址
                    folder_path = homeName
                    # 检查文件夹是否存在
                    if not os.path.exists(folder_path):
                        # 如果不存在，则创建文件夹
                        os.makedirs(folder_path)
                        # self.tip_text.append("文件夹 " + folder_path + " 创建成功。")
                        print(f"文件夹 '{folder_path}' 创建成功。")
                    else:
                        # self.tip_text.append("文件夹 " + folder_path + " 已存在。")
                        print(f"文件夹 '{folder_path}' 已存在。")
                    output_filename = folder_path + '/' + homeName + '.mp4'

                    # 发送请求并获取响应
                    response = requests.get(needDown, stream=True)

                    # 获取文件大小
                    total_size_in_bytes = int(response.headers.get('content-length', 0))

                    # 检查请求是否成功
                    if response.status_code == 200:
                        # 使用tqdm创建一个进度条
                        progress_bar = tqdm(desc='正在获取' + homeName + '的内容',
                                            total=total_size_in_bytes,
                                            unit='iB',
                                            unit_scale=True,
                                            mininterval=1)

                        # 以二进制模式打开文件，并写入数据
                        async with aiofiles.open(output_filename, 'wb') as f:
                            for data in response.iter_content(chunk_size=1024):
                                size = await f.write(data)  # 异步等待
                                progress_bar.update(size)

                        # 关闭进度条
                        progress_bar.close()

                        print(f"视频已成功下载为 {output_filename}")
                        # self.tip_text.append("视频已成功下载为 " + output_filename)
                    else:
                        print("下载失败，状态码:", response.status_code)
                        # self.tip_text.append("下载失败，状态码:" + response.status_code)
            else:
                print("正在查找直播间信息...")
                i = i+1
                length = len(script_tags)
                if i == length:
                    print("未找到直播间信息,此账号已被屏蔽pc端直播观看权限!请切换账号进行登录!")
                # self.tip_text.append("未找到直播间信息,此账号已被屏蔽pc端直播观看权限!请切换账号进行登录!")

    else:
        print("请求失败，状态码:", response.status_code)
        # self.tip_text.append("请求失败，状态码:" + str(response.status_code))


# 读取pddAdr.txt文件的所有直播间 并行下载
async def startVideoDownload():
    # 打开文件并读取每一行
    with open('pddAdr.txt', 'r', encoding='utf-8') as file:
        urls = file.readlines()
    await asyncio.gather(*[downVideoByUrl(url) for url in urls])


class PddRecordingApp(QWidget):
    def __init__(self):
        super().__init__()
        self.tip_text = None
        self.code_entry = None
        self.phone_entry = None
        self.initUI()

    def initUI(self):
        self.setWindowTitle("pdd直播流录制软件")
        self.resize(500, 500)

        # 上部分
        top_frame = QFrame(self)
        top_frame.setStyleSheet("")
        top_frame.setGeometry(0, 0, self.width(), self.height() * 0.1)

        phone_label = QLabel("手机号:", top_frame)
        phone_label.move(10, 10)

        self.phone_entry = QLineEdit(top_frame)
        self.phone_entry.setFixedWidth(120)
        self.phone_entry.move(60, 10)

        code_label = QLabel("验证码:", top_frame)
        code_label.move(200, 10)

        self.code_entry = QLineEdit(top_frame)
        self.code_entry.setFixedWidth(80)
        self.code_entry.move(260, 10)

        # 中部分
        middle_frame = QFrame(self)
        middle_frame.setStyleSheet("")
        middle_frame.setGeometry(0, self.height() * 0.1, self.width(), self.height() * 0.1)

        send_code_button = QPushButton("发送验证码", middle_frame)
        send_code_button.setFixedWidth(80)
        send_code_button.setFixedHeight(30)
        send_code_button.setStyleSheet("background-color: #FFFFFF;")
        send_code_button.clicked.connect(self.send_code_clicked)
        send_code_button.move(10, 10)

        login_button = QPushButton("登录", middle_frame)
        login_button.setFixedWidth(80)
        login_button.setFixedHeight(30)
        login_button.setStyleSheet("background-color: #FFFFFF;")
        login_button.clicked.connect(self.login_clicked)
        login_button.move(100, 10)

        record_button = QPushButton("开始录制", middle_frame)
        record_button.setFixedWidth(80)
        record_button.setFixedHeight(30)
        record_button.setStyleSheet("background-color: #FFFFFF;")
        record_button.clicked.connect(self.record_clicked)
        record_button.move(190, 10)

        config_button = QPushButton("更改配置文件", middle_frame)
        config_button.setFixedWidth(120)
        config_button.setFixedHeight(30)
        config_button.setStyleSheet("background-color: #FFFFFF;")
        config_button.clicked.connect(self.config_clicked)
        config_button.move(280, 10)

        # 下部分
        bottom_frame = QFrame(self)
        bottom_frame.setStyleSheet("")
        bottom_frame.setGeometry(0, self.height() * 0.2, self.width(), self.height() * 0.8)

        self.tip_text = QTextEdit(bottom_frame)
        self.tip_text.setReadOnly(True)
        # self.tip_text.setText("这是要插入的文字，它会自动换行。\n")
        self.tip_text.setGeometry(10, 10, bottom_frame.width() - 20, bottom_frame.height() - 20)
        self.tip_text.append("欢迎来使用本软件!")
        self.tip_text.append("正在检测登录信息...")
        hasCK = judgeHasCk(self)
        if hasCK:
            self.tip_text.append("检测到登录信息,无需再次登录,可直接开始录制")
        else:
            self.tip_text.append("未检测到登录信息,请重新登录...")

    def setTipText(self, text):
        self.tip_text.append(text)

    def judgeHasCk(self):
        return judgeHasCk(self)

    def send_code_clicked(self):
        # 获取手机号
        phone_number = self.phone_entry.text()
        if phone_number == "":
            self.tip_text.append("手机号不能为空")
            return
        self.tip_text.clear()
        sentCodeWithPhone(phone_number, self)

    def login_clicked(self):
        # 获取手机号
        phone_number = self.phone_entry.text()
        if phone_number == "":
            self.tip_text.append("手机号不能为空")
            return
        # 获取验证码
        code_number = self.code_entry.text()
        if code_number == "":
            self.tip_text.append("验证码不能为空")
            return
        # 登录第一步  获取cookies
        pddCookies = getCookiesWithCodeAndLogin(phone_number, code_number, self)
        # 保存登录信息
        saveCookies(pddCookies, self)

    def record_clicked(self):
        ck = getCkFromTxt()
        if ck is None or ck == "":
            self.tip_text.append("未检测到登录信息,请重新登录...")
            return
        # 运行异步程序   全部下载
        asyncio.run(startVideoDownload())

    def config_clicked(self):
        print("更改配置文件")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = PddRecordingApp()
    ex.show()
    sys.exit(app.exec_())
